/** \file GameController.cpp
 *  \author Adrian Schweizer
 *  \created  $Sa 06 Okt 04:47:32 pm CEST 2007 schwadri@SchwadriLaptop$
 *  \modified $Sa 20 Okt 07:10:46 pm CEST 2007 schwadri@SchwadriLaptop$
 */

#include <boost/bind.hpp>

#include "../network/NetworkController.hpp"
#include "../core/time.hpp"
#include "../world/Simulation.hpp"
#include "GameController.hpp"
#include "ActionDispatcher.hpp"

using namespace game;

class GameController::Impl
{
public:
    Impl(GameController* gc)
    :   m_dispatch(*gc)
    {
    }

    action::Dispatcher m_dispatch;
};

GameController::GameController()
:   m_impl(new Impl(this)),
    m_mode(LOBBY),
    m_network(&network::NetworkController::getNetworkController())
    //m_dispatch(*this)
{
    //connect network signals
    m_network->ConnectedSignal.connect(boost::bind(&GameController::OnConnected, this, _1));
    m_network->DisonnectedSignal.connect(boost::bind(&GameController::OnDisconnected, this, _1));
    m_network->ClientConnectedSignal.connect(boost::bind(&GameController::OnClientConnected, this, _1));
    m_network->ClientDisconnectedSignal.connect(boost::bind(&GameController::OnClientDisconnected, this, _1, _2));
    //m_network->
}

GameController::~GameController()
{
    delete m_impl;
}

void GameController::HostGame(const network::Address& addr, const std::string& server_name)
{
    if(m_network->Host(addr,/*FIXME:*/4))
    {
        m_mode = LOBBY;
        //set up LOBBY
        m_run = true;
        RunLobby();
    }
    else
    {
        //something went wrong
        std::cout << "unable to start hosting"<<std::endl;
    }
}

void GameController::JoinGame(const network::Address& addr)
{
    if(m_network->Connect(addr))
    {
        m_run = true;
        //TODO: show cancel button etc.
        while(m_run)
        {
            m_network->ProcessMessages();
            //m_gui->Update();
        }
    }
    else
    {
        //something went wrong
        std::cout << "unable to connect"<<std::endl;
    }
}

void GameController::RunLobby()
{
    while(m_run)
    {
        m_network->ProcessMessages();
        //m_gui->Update();
    }
}

bool collision_dummy_callback(const world::Collision& c)
{
    std::cout << "Collision { p=" << c.position << " n=" << c.n << " t1=" << c.t1 << " t2=" << c.t2 << " } " << std::endl;
    //exit(EXIT_SUCCESS);
    return true;
}

void GameController::RunGame()
{
    //FIXME:
    world::Simulation* m_simulation = new world::Simulation(collision_dummy_callback,world::Simulation::Options());
    int start_ticks = core::time::ticks();
    int last_ticks = start_ticks;
    m_run = true;
    using namespace world;

    Entity* e;
    m_simulation->Add(e=new Entity(1.0,                           //mass
                                     matrix3r(  1.0,0.0,0.0,    //inertia matrix
                                                0.0,1.0,0.0,
                                                0.0,0.0,1.0),
                                     vector3r(0.0,0.0,0.0),     //position
                                     quatr(1.0,0.0,0.0,0.0),    //orientation
                                     vector3r(1.0,0.0,0.0),     //velocity
                                     vector3r(0.0,0.0,0.0)));   //angular velocity
    boost::shared_ptr<Geometry> g(new Geometry(1.0));
    e->Geom(g);
    m_simulation->Add(e=new Entity(1.0,                           //mass
                                     matrix3r(  1.0,0.0,0.0,    //inertia matrix
                                                0.0,1.0,0.0,
                                                0.0,0.0,1.0),
                                     vector3r(3.0,0.0,0.0),     //position
                                     quatr(1.0,0.0,0.0,0.0),    //orientation
                                     vector3r(0.0,0.0,0.0),     //velocity
                                     vector3r(0.0,0.0,0.0)));   //angular velocity
    g.reset(new Geometry(vector3r(1.0,1.0,1.0),vector3r(0.0,0.0,0.0)));
    e->Geom(g);
    while(m_run)
    {
        m_network->ProcessMessages();
        //m_gui->Update();
        int ticks = core::time::ticks();
        world::real    dt  =   (ticks-last_ticks)/1e3f;
        m_simulation->Update(dt);
        last_ticks = ticks;
    }
}

void GameController::OnClientDisconnected(network::Client::Id cid, network::DisconnectReason reason)
{
    if(m_network->IsServer())
    {
    }
    else
    {
    }
}

void GameController::OnClientConnected(network::Client::Id cid)
{
    if(m_network->IsServer())
    {
        //TODO: Check if there is any space left for a new player in a team or in the spectators group
        
        //send game options
        m_impl->m_dispatch.Send(action::GameChangeOptions(m_options,network::receiver_mask(cid)));//,network::Client::SERVER);

        //send all connected players
        
        
        //send new player
    }
    else
    {
    }
}

void GameController::OnConnected(network::Client::Id cid)
{
    if(m_network->IsServer())
        OnClientConnected(cid);
    else
    {
        m_mode = CONNECTED;
        m_my_cid = cid;
        //send change player
        //send change name
        action::ClientChangeName ccna(m_my_cid,"stinkender wixer");
        m_impl->m_dispatch.Dispatch(ccna,m_my_cid);
    }
}

void GameController::OnDisconnected(network::DisconnectReason reason)
{
    if(m_network->IsServer())
    {
        //ignore. this should not happen
        throw "Hallo? Hey ich wott dem Signal nöd!";
    }
    else
    {
        //show info dialoge
        switch(reason)
        {
            case network::KICKED:
                std::cout << ">> Kicked\n";
                break;
            case network::BANNED:
                std::cout << ">> Banned\n";
                break;
            case network::TIMED_OUT:
                std::cout << ">> Connection Timed Out\n";
                break;
            case network::CLOSED:
                std::cout << ">> Connection Closed\n";
                break;
            case network::CONNECTION_FAILED:
                std::cout << ">> Connection Failed\n";
                break;
            case network::UNKNOWN:
                std::cout << ">> Connection closed for unknown reason\n";
                break;
            case network::VERSION_MISMATCH:
                std::cout << ">> Version Mismatch\n";
                break;
            case network::SERVER_FULL:
                std::cout << ">> Server Full\n";
                break;
            case network::WRONG_PASSWORD:
                std::cout << ">> Wrong Password\n";
                break;
            default:
                throw "Urk!";
        }
        
        switch(m_mode)
        {
            case CONNECTING:
                std::cout << ">> Could not Connect to Server" << std::endl;
                m_run = false;
                break;
            case GAME:
            case LOBBY:
                m_run = false;
                break;
            case DONE:
            default:
                break;
        }
    }
}

